package com.stars.easyms.base.util;

import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.*;

/**
 * <p>className: ExecutorUtil</p>
 * <p>description: 线程池工具类</p>
 *
 * @author guoguifang
 * @version 1.2.1
 * @date 2019-07-09 14:04
 */
@Slf4j
public final class ExecutorUtil {

    private static EasyMsThreadPoolExecutor executorService;

    public static void execute(Runnable command) {
        getExecutorService().execute(command);
    }

    public static <T> Future<T> submit(Callable<T> task) {
        return getExecutorService().submit(task);
    }

    public static <T> Future<T> submit(Runnable task, T result) {
        return getExecutorService().submit(task, result);
    }

    public static Future submit(Runnable command) {
        return getExecutorService().submit(command);
    }

    /**
     * 执行所有回调任务，并把future集合返回，没有等待
     */
    public static <T> List<Future<T>> submit(Collection<? extends Callable<T>> tasks) {
        List<Future<T>> result = new ArrayList<>(tasks.size());
        tasks.forEach(task -> result.add(getExecutorService().submit(task)));
        return result;
    }

    /**
     * 执行所有回调任务，只有当所有任务全部执行完才把future集合返回，有等待
     */
    public static <T> List<Future<T>> submitAndDone(Collection<? extends Callable<T>> tasks) throws InterruptedException {
        return getExecutorService().invokeAll(tasks);
    }

    /**
     * 执行所有回调任务，当所有任务执行完或超时后把future集合返回，有等待
     */
    public static <T> List<Future<T>> submitAndDone(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException {
        return getExecutorService().invokeAll(tasks, timeout, unit);
    }

    public static <T> T submitAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
        return getExecutorService().invokeAny(tasks);
    }

    public static <T> T submitAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
        return getExecutorService().invokeAny(tasks, timeout, unit);
    }

    /**
     * 完成所有Future任务并把结果返回
     *
     * @since 1.3.3
     */
    public static <T> List<T> done(List<Future<T>> futureList) throws InterruptedException {
        List<T> result = new ArrayList<>(futureList.size());
        for (Future<T> future : futureList) {
            T t = null;
            try {
                t = future.get();
            } catch (ExecutionException e) {
                log.error("Future get failure!", e);
                future.cancel(true);
            } catch (CancellationException ignore) {
                // ignore
            }
            result.add(t);
        }
        return result;
    }

    /**
     * 完成所有Future任务并把结果返回
     *
     * @since 1.3.3
     */
    public static <T> List<T> done(List<Future<T>> futureList, long timeout, TimeUnit unit) throws InterruptedException {
        List<T> result = new ArrayList<>(futureList.size());
        for (Future<T> future : futureList) {
            T t = null;
            try {
                t = future.get(timeout, unit);
            } catch (ExecutionException e) {
                log.error("Task execute failure!", e);
                future.cancel(true);
            } catch (CancellationException ignore) {
                // ignore
            } catch (TimeoutException e) {
                log.error("Future result get timeout!", e);
                future.cancel(true);
            }
            result.add(t);
        }
        return result;
    }

    /**
     * 判断所有Future任务是否已经处理完
     *
     * @since 1.3.3
     */
    public static boolean isDone(List<Future> futureList) {
        for (Future future : futureList) {
            if (!future.isDone()) {
                return false;
            }
        }
        return true;
    }

    public static int getActiveCount() {
        return executorService.getActiveCount();
    }

    private static ExecutorService getExecutorService() {
        EasyMsThreadPoolExecutor localExecutorService = executorService;
        if (localExecutorService == null) {
            synchronized (ExecutorUtil.class) {
                localExecutorService = executorService;
                if (localExecutorService == null) {
                    executorService = localExecutorService = new EasyMsThreadPoolExecutor(2, 200, 60L, TimeUnit.SECONDS,
                            new DefaultThreadFactory().setNameFormat("easyms-asyn-%d").build());
                }
            }
        }
        return localExecutorService;
    }

    private ExecutorUtil() {
    }
}